Skip to content

tests: Extend test framework for driverless board qualification scenarios.#89

Merged
nedseb merged 23 commits intomainfrom
feature/board-qualification-framework
Mar 13, 2026
Merged

tests: Extend test framework for driverless board qualification scenarios.#89
nedseb merged 23 commits intomainfrom
feature/board-qualification-framework

Conversation

@nedseb
Copy link
Copy Markdown
Contributor

@nedseb nedseb commented Mar 13, 2026

Summary

Closes #83

Extends the test framework to support board-level qualification tests — scenarios that test hardware components (LEDs, buttons, buzzer, pins, I2C bus) without requiring a driver.

Changes

  • tests/conftest.py: Register board pytest marker for filtering
  • tests/runner/mpremote_bridge.py: Add run_raw_script(script) method that executes raw MicroPython code on the board without I2C/driver initialization
  • tests/test_scenarios.py:
    • iter_scenario_tests(): Handle type: board YAML scenarios (hardware-only, board + hardware markers)
    • test_scenario(): Route board hardware_script actions to run_raw_script(), guard unsupported actions (call, read_register, interactive) for board scenarios
    • _print_result(): Skip printing bare boolean results (not informative for smoke tests)

New scenario format

Board scenarios use type: board and have no driver/driver_class:

type: board
name: "board_example"

tests:
  - name: "Test something"
    action: hardware_script
    script: |
      from machine import Pin
      result = True
    expect_true: true
    mode: [hardware]

Test suite separation

Suite Command Purpose
CI/PR pytest tests/ -k mock Mock tests (unchanged)
Driver HW pytest tests/ --port ... -k "not board" Driver hardware tests
Board qualification pytest tests/ --port ... -k board -s -v Board qualification (test bench)

How to test

# Mock tests (no regression)
pytest tests/ -k mock

# Collect board tests (no hardware needed)
pytest tests/ -k board --collect-only

# Run all board qualification tests on hardware
pytest tests/ --port /dev/ttyACM0 -k board -s -v

# Existing driver tests unchanged
pytest tests/ -k "not board"

Test results

$ pytest tests/ -k mock -q
41 passed, 95 deselected in 0.57s

$ pytest tests/ -k board --collect-only -q
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer tone 440Hz/hardware]
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer 440Hz audible/hardware]
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer frequency sweep/hardware]
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer sweep audible/hardware]
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer Ode to Joy/hardware]
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer Ode to Joy audible/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/Button A press (polling)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/Button B press (polling)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/Button MENU press (polling)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD UP press (polling)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD DOWN press (polling)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD LEFT press (polling)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD RIGHT press (polling)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/Button A press (interrupt)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/Button B press (interrupt)/hardware]
tests/test_scenarios.py::test_scenario[board_buttons/Button MENU press (interrupt)/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/I2C bus scan finds expected devices/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/No unexpected I2C devices/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/LIS2MDL WHO_AM_I (0x1E)/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/MCP23009E IODIR default (0x20)/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/VL53L1X model ID (0x29)/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/APDS9960 device ID (0x39)/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/WSEN-PADS device ID (0x5D)/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/HTS221/WSEN-HIDS device ID (0x5F)/hardware]
tests/test_scenarios.py::test_scenario[board_i2c_scan/ISM330DL WHO_AM_I (0x6B)/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED RED on|off/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED RED visible/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED GREEN on|off/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED GREEN visible/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED BLUE on|off/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED BLUE visible/hardware]
tests/test_scenarios.py::test_scenario[board_leds/RGB LED full cycle/hardware]
tests/test_scenarios.py::test_scenario[board_leds/RGB LED full cycle visible/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED BLE on|off/hardware]
tests/test_scenarios.py::test_scenario[board_leds/LED BLE visible/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P0 (PC4) analog read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P1 (PA5) analog read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P2 (PC5) analog read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P3 (PA2) analog read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P4 (PA4) analog read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P10 (PA6) analog read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P6 (PC3) digital pull-up read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P7 (PA9) digital pull-up read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P8 (PA15) digital pull-up read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P9 (PC2) digital pull-up read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P12 (PC6) digital pull-up read/hardware]
tests/test_scenarios.py::test_scenario[board_pins/Pin P16 (PE4) digital pull-up read/hardware]
47 tests collected

$ pytest tests/ --port /dev/ttyACM0 -k board -s -v
============================== test session starts ==============================
platform linux -- Python 3.13.7, pytest-8.3.5, pluggy-1.5.0 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/nedjar/sandbox/micropython-steami-lib
configfile: pytest.ini
plugins: typeguard-4.4.2
collected 150 items / 103 deselected / 47 selected                                                                                                                                                                                                                          

tests/test_scenarios.py::test_scenario[board_buttons/Button A press (polling)/hardware]   [INTERACTIVE] Test polling boutons : appuyez sur chaque bouton quand la LED verte s'allume (5s). Commençons par A.
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/Button B press (polling)/hardware]   [INTERACTIVE] Bouton B
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/Button MENU press (polling)/hardware]   [INTERACTIVE] Bouton MENU
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD UP press (polling)/hardware]   [INTERACTIVE] Test polling D-PAD : appuyez sur chaque bouton quand la LED verte s'allume (5s). Commençons par UP.
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD DOWN press (polling)/hardware]   [INTERACTIVE] Bouton DOWN
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD LEFT press (polling)/hardware]   [INTERACTIVE] Bouton LEFT
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/D-PAD RIGHT press (polling)/hardware]   [INTERACTIVE] Bouton RIGHT
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/Button A press (interrupt)/hardware]   [INTERACTIVE] Test interrupt boutons : appuyez brièvement sur chaque bouton quand la LED verte s'allume (5s). Commençons par A.
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/Button B press (interrupt)/hardware]   [INTERACTIVE] Bouton B
PASSED
tests/test_scenarios.py::test_scenario[board_buttons/Button MENU press (interrupt)/hardware]   [INTERACTIVE] Bouton MENU
PASSED
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer tone 440Hz/hardware] PASSED
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer 440Hz audible/hardware]   [MANUAL] Avez-vous entendu un bip (La 440Hz) ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer frequency sweep/hardware] PASSED
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer sweep audible/hardware]   [MANUAL] Avez-vous entendu un balayage de fréquence (grave vers aigu) ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer Ode to Joy/hardware] PASSED
tests/test_scenarios.py::test_scenario[board_buzzer/Buzzer Ode to Joy audible/hardware]   [MANUAL] Avez-vous reconnu l'Hymne à la joie (Beethoven) ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/I2C bus scan finds expected devices/hardware] []
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/No unexpected I2C devices/hardware] []
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/LIS2MDL WHO_AM_I (0x1E)/hardware] 0x40
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/MCP23009E IODIR default (0x20)/hardware] 0xFF
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/VL53L1X model ID (0x29)/hardware] 0xEACC
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/APDS9960 device ID (0x39)/hardware] 0xAB
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/WSEN-PADS device ID (0x5D)/hardware] 0xB3
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/HTS221/WSEN-HIDS device ID (0x5F)/hardware] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[board_i2c_scan/ISM330DL WHO_AM_I (0x6B)/hardware] 0x6A
PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED RED on/off/hardware]   [INTERACTIVE] Appuyez sur Entrée pour lancer le test LED RGB, puis observez la carte. 
PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED RED visible/hardware]   [MANUAL] La LED rouge s'est-elle allumée puis éteinte ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED GREEN on/off/hardware] PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED GREEN visible/hardware]   [MANUAL] La LED verte s'est-elle allumée puis éteinte ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED BLUE on/off/hardware] PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED BLUE visible/hardware]   [MANUAL] La LED bleue s'est-elle allumée puis éteinte ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_leds/RGB LED full cycle/hardware] PASSED
tests/test_scenarios.py::test_scenario[board_leds/RGB LED full cycle visible/hardware]   [MANUAL] La LED a-t-elle affiché rouge, vert, bleu, puis blanc ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED BLE on/off/hardware]   [INTERACTIVE] Appuyez sur Entrée pour lancer le test LED BLE, puis observez la carte. 
PASSED
tests/test_scenarios.py::test_scenario[board_leds/LED BLE visible/hardware]   [MANUAL] La LED BLE (bleue) s'est-elle allumée puis éteinte ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P0 (PC4) analog read/hardware] 51180
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P1 (PA5) analog read/hardware] 38761
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P2 (PC5) analog read/hardware] 27366
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P3 (PA2) analog read/hardware] 32984
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P4 (PA4) analog read/hardware] 53052
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P10 (PA6) analog read/hardware] 25254
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P6 (PC3) digital pull-up read/hardware] 1
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P7 (PA9) digital pull-up read/hardware] 1
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P8 (PA15) digital pull-up read/hardware] 1
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P9 (PC2) digital pull-up read/hardware] 1
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P12 (PC6) digital pull-up read/hardware] 1
PASSED
tests/test_scenarios.py::test_scenario[board_pins/Pin P16 (PE4) digital pull-up read/hardware] 1
PASSED

====================== 47 passed, 103 deselected in 54.41s ======================

$ pytest tests/ --port /dev/ttyACM0 -k "not board" -s -v
============================== test session starts ==============================
platform linux -- Python 3.13.7, pytest-8.3.5, pluggy-1.5.0 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/nedjar/sandbox/micropython-steami-lib
configfile: pytest.ini
plugins: typeguard-4.4.2
collected 150 items / 47 deselected / 103 selected                                                                                                                                                                                                                          

tests/test_scenarios.py::test_scenario[apds9960/Verify device ID register (mock)/mock] 0xAB
PASSED
tests/test_scenarios.py::test_scenario[apds9960/Verify device ID register (hardware)/hardware] 0xAB
PASSED
tests/test_scenarios.py::test_scenario[apds9960/Read ambient light returns expected value/mock] 0x0100
PASSED
tests/test_scenarios.py::test_scenario[apds9960/Read proximity returns expected value/mock] 0x50
PASSED
tests/test_scenarios.py::test_scenario[apds9960/Read red light returns expected value/mock] 0x80
PASSED
tests/test_scenarios.py::test_scenario[apds9960/Ambient light in plausible range/hardware] 871
PASSED
tests/test_scenarios.py::test_scenario[apds9960/Proximity in plausible range/hardware] 19
PASSED
tests/test_scenarios.py::test_scenario[apds9960/Light and proximity values feel correct/hardware]   Ambient light: 869 
  Proximity: 21 
  [MANUAL] Les valeurs de lumière ambiante et de proximité sont-elles cohérentes ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Read voltage/mock] 0x0E74
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Read state of charge/mock] 0x5C
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Read remaining capacity/mock] 0x0258
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Read full capacity/mock] 0x028A
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Read average current/mock] 0x32
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Read average power/mock] 0xB9
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Read state of health/mock] 0x63
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Battery connected check/hardware]   [MANUAL] Une batterie est-elle connectée à la carte ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Device type is BQ27441/hardware] PASSED
tests/test_scenarios.py::test_scenario[bq27441/Voltage in plausible range/hardware] 4176
PASSED
tests/test_scenarios.py::test_scenario[bq27441/State of charge in valid range/hardware] 100
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Remaining capacity positive/hardware] 1208
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Full capacity positive/hardware] 1208
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Average current readable/hardware] 0
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Average power readable/hardware] 0
PASSED
tests/test_scenarios.py::test_scenario[bq27441/State of health in valid range/hardware] 90
PASSED
tests/test_scenarios.py::test_scenario[bq27441/Battery info summary/hardware]   Tension: 4176 mV
  Charge: 100 %
  Capacité restante: 1208 mAh
  Capacité totale: 1208 mAh
  Courant moyen: 0 mA
  Santé batterie: 90 %
  [MANUAL] Ces valeurs sont-elles cohérentes avec l'état de la batterie ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[hts221/Verify device ID/mock] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[hts221/Verify device ID/hardware] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[hts221/Read WHO_AM_I via method/mock] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[hts221/Read WHO_AM_I via method/hardware] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[hts221/Read temperature returns float/mock] -5292.72
PASSED
tests/test_scenarios.py::test_scenario[hts221/Read humidity returns float/mock] 53.83
PASSED
tests/test_scenarios.py::test_scenario[hts221/Temperature in plausible range/hardware] 30.80
PASSED
tests/test_scenarios.py::test_scenario[hts221/Humidity in plausible range/hardware] 38.25
PASSED
tests/test_scenarios.py::test_scenario[hts221/Status register has data ready/mock] 3
PASSED
tests/test_scenarios.py::test_scenario[hts221/Status register has data ready/hardware] 3
PASSED
tests/test_scenarios.py::test_scenario[hts221/Temperature feels correct/hardware]   Temperature: 30.54 °C
  Humidity: 37.72 %
  [MANUAL] Ces valeurs sont-elles cohérentes avec l'ambiance ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Verify WHO_AM_I register/mock] 0x40
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Verify WHO_AM_I register/hardware] 0x40
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Read WHO_AM_I via method/mock] 0x40
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Read WHO_AM_I via method/hardware] 0x40
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Read status register/mock] 15
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Read status register/hardware] 255
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Read magnetic field returns tuple/mock] (300, -150, 450)
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Read magnetic field in uT returns tuple/mock] (45.0, -22.5, 67.5)
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Read temperature returns float/mock] 25.00
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Magnitude in plausible range/hardware] 167.27
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Temperature in plausible range/hardware] 3.00
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Soft reset then WHO_AM_I/hardware] 0x40
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Reboot then WHO_AM_I/hardware] 0x40
PASSED
tests/test_scenarios.py::test_scenario[lis2mdl/Magnetic field values feel correct/hardware]   Magnitude: 0.21 µT
  Temperature: 3.00 °C
  Heading: 237.70 °
  [MANUAL] Ces valeurs sont-elles cohérentes (magnitude ~25-65µT, cap 0-360°) ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/Read IODIR default value/mock] 0xFF
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/Read IODIR default value/hardware] 0xFF
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/Read GPIO register/mock] 240
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/Read GPIO register/hardware] 240
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/Read individual GPIO level/mock] 0
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD buttons state/hardware]   UP (GP7): 1 (1=relâché, 0=appuyé)
  DOWN (GP5): 1 (1=relâché, 0=appuyé)
  LEFT (GP6): 1 (1=relâché, 0=appuyé)
  RIGHT (GP4): 1 (1=relâché, 0=appuyé)
  [MANUAL] Tous les boutons sont-ils relâchés (valeur 1) ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD UP button (polling)/hardware]   [INTERACTIVE] Test polling D-PAD : appuyez sur chaque bouton quand la LED verte s'allume (5s). Commençons par UP. 
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD DOWN button (polling)/hardware]   [INTERACTIVE] Bouton DOWN
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD LEFT button (polling)/hardware]   [INTERACTIVE] Bouton LEFT
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD RIGHT button (polling)/hardware]   [INTERACTIVE] Bouton RIGHT
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD UP button (interrupt)/hardware]   [INTERACTIVE] Test interrupt D-PAD : appuyez brièvement sur chaque bouton quand la LED verte s'allume (5s). Commençons par UP. 
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD DOWN button (interrupt)/hardware]   [INTERACTIVE] Bouton DOWN
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD LEFT button (interrupt)/hardware]   [INTERACTIVE] Bouton LEFT
PASSED
tests/test_scenarios.py::test_scenario[mcp23009e/D-PAD RIGHT button (interrupt)/hardware]   [INTERACTIVE] Bouton RIGHT
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Fill black does not crash/mock] None
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Fill white does not crash/mock] None
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Text rendering does not crash/mock] None
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Show does not crash/mock] None
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Display white screen/hardware] PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Screen is white/hardware]   [MANUAL] L'écran affiche-t-il un fond blanc uniforme ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Display text/hardware] PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Text is visible/hardware]   [MANUAL] L'écran affiche-t-il 'STeaMi' et 'Test OK' sur fond noir ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Display grayscale gradient/hardware] PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Grayscale gradient is visible/hardware]   [MANUAL] L'écran affiche-t-il 16 bandes verticales du noir au blanc ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Scrolling animation/hardware] PASSED
tests/test_scenarios.py::test_scenario[ssd1327/Animation was smooth/hardware]   [MANUAL] Le texte 'STeaMi' a-t-il defile horizontalement de maniere fluide ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[vl53l1x/Verify model ID register/mock] 0xEACC
PASSED
tests/test_scenarios.py::test_scenario[vl53l1x/Verify model ID register/hardware] 0xEACC
PASSED
tests/test_scenarios.py::test_scenario[vl53l1x/Read distance returns expected value/mock] 0xFA
PASSED
tests/test_scenarios.py::test_scenario[vl53l1x/Distance in plausible range/hardware] 1621
PASSED
tests/test_scenarios.py::test_scenario[vl53l1x/Distance measurement feels correct/hardware]   Distance: 1615 mm
  [MANUAL] La distance mesurée est-elle cohérente (objet devant le capteur) ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Verify device ID register/mock] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Verify device ID register/hardware] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Read device ID via method/mock] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Read device ID via method/hardware] 0xBC
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Read status register/mock] 3
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Read status register/hardware] 3
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Read humidity returns float/mock] 50.00
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Read temperature returns float/mock] 25.00
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Humidity in plausible range/hardware] 39.80
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Temperature in plausible range/hardware] 20.18
PASSED
tests/test_scenarios.py::test_scenario[wsen-hids/Humidity and temperature feel correct/hardware]   Humidity: 39.76 %RH
  Temperature: 20.18 °C
  [MANUAL] Ces valeurs sont-elles cohérentes (humidité ambiante, température ambiante) ? [Entree=oui / Echap=non] 
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Verify device ID register/mock] 0xB3
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Verify device ID register/hardware] 0xB3
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Read device ID via method/mock] 0xB3
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Read device ID via method/hardware] 0xB3
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Read status register/mock] 3
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Read status register/hardware] 1
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Read pressure returns float/mock] 1013.25
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Read temperature returns float/mock] 25.00
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Pressure in plausible range/hardware] 999.79
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Temperature in plausible range/hardware] 30.46
PASSED
tests/test_scenarios.py::test_scenario[wsen-pads/Pressure and temperature feel correct/hardware]   Pressure: 999.69 hPa
  Temperature: 30.52 °C
  [MANUAL] Ces valeurs sont-elles cohérentes (pression ~1013 hPa, température ambiante) ? [Entree=oui / Echap=non] 
PASSED

================ 103 passed, 47 deselected in 374.24s (0:06:14) =================

Test plan

  • ruff check tests/ — passed
  • pytest tests/ -k mock — 41 passed (no regression)
  • pytest tests/ -k board (sans --port) — board tests correctly skipped
  • pytest tests/ -k "not board" — existing driver tests unchanged
  • Hardware validation — all 47 board qualification tests passed on STeaMi board

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support in the YAML scenario runner for “board qualification” scenarios that run on real hardware without a driver context.

Changes:

  • Extend scenario parametrization to support type: board, generate friendlier scenario names, and apply a board marker.
  • Add MpremoteBridge.run_raw_script() to execute hardware scripts without mounting/initializing a driver.
  • Register the new board pytest marker in tests/conftest.py.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
tests/test_scenarios.py Adds board-type scenario handling (hardware-only filtering, board mark, and raw-script execution for hardware_script).
tests/runner/mpremote_bridge.py Introduces run_raw_script() for running plain MicroPython scripts and JSON-decoding result.
tests/conftest.py Registers the board marker for pytest.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_scenarios.py Outdated
Comment thread tests/test_scenarios.py Outdated
@nedseb
Copy link
Copy Markdown
Contributor Author

nedseb commented Mar 13, 2026

Corrections suite à la review Copilot (bc70f05) :

  1. Marker hardware ajouté : les board scenarios ont maintenant [pytest.mark.board, pytest.mark.hardware], ce qui les rend compatibles avec pytest -m hardware et le skip automatique dans pytest_collection_modifyitems.

  2. Guard contre les actions driver-only : les actions call, read_register et interactive émettent un pytest.fail() explicite si utilisées dans un board scenario. L'action manual fonctionne pour les board scenarios mais ignore le bloc display (qui nécessite un driver).

@nedseb
Copy link
Copy Markdown
Contributor Author

nedseb commented Mar 13, 2026

Réponses aux commentaires Copilot :

Board scenarios manquent le marker hardware — Corrigé : les board scenarios reçoivent [pytest.mark.board, pytest.mark.hardware], donc -k board et -k hardware fonctionnent, et --port est requis.

Actions non supportées (call, read_register, interactive) crasheraient sur board scenarios — Corrigé : ajout d'un guard pytest.fail() avec message explicite pour ces actions. Le block display des tests manual est aussi protégé par un if not is_board.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “board qualification” scenario type to the YAML scenario test framework, enabling hardware-only checks that run raw MicroPython scripts without requiring a driver context.

Changes:

  • Extend tests/test_scenarios.py to support type: board scenarios (hardware-only), including new param IDs/marks and raw-script execution.
  • Add MpremoteBridge.run_raw_script() to execute board scripts without mounting/importing a driver.
  • Introduce new board qualification scenario YAMLs (pins, LEDs, I2C scan/IDs, buzzer, buttons) plus a new board pytest marker.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/test_scenarios.py Adds type: board handling, adjusts test IDs/marks, and routes board hardware_script to raw execution.
tests/runner/mpremote_bridge.py Adds run_raw_script() for executing scripts without driver context and returning JSON-decoded result.
tests/conftest.py Registers new board marker.
tests/scenarios/board_pins.yaml Adds hardware-only smoke tests for ADC and digital pull-up reads on board pins.
tests/scenarios/board_leds.yaml Adds hardware-script + manual verification coverage for board LEDs.
tests/scenarios/board_i2c_scan.yaml Adds I2C scan and basic ID/register presence checks for expected devices.
tests/scenarios/board_buzzer.yaml Adds buzzer tone/sweep/melody scripts with manual confirmation steps.
tests/scenarios/board_buttons.yaml Adds polling + interrupt button tests, including D-PAD reads via MCP23009E.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_scenarios.py
Comment thread tests/test_scenarios.py
Comment thread tests/scenarios/board_buttons.yaml
Comment thread tests/scenarios/board_buttons.yaml
Comment thread tests/scenarios/board_buttons.yaml
Comment thread tests/scenarios/board_buttons.yaml
@nedseb
Copy link
Copy Markdown
Contributor Author

nedseb commented Mar 13, 2026

Réponses aux commentaires Copilot (2e review) :

_print_result() retourne vide pour les booléens — Voulu : les smoke tests retournant True/False n'ont pas de valeur mesurée utile à afficher. Les tests avec des valeurs numériques (ADC, registres, listes) continuent d'être affichés normalement.

Markers hardware sur les driver scenarios — Les driver scenarios sont filtrés par mode dans le YAML, pas par pytest markers. Le marker hardware est ajouté uniquement aux board scenarios pour permettre le filtrage -k board / -k hardware. C'est cohérent avec le fonctionnement existant.

D-PAD tests sans reset du MCP23009E — Corrigé : ajout de RST_EXPANDER release dans le premier test D-PAD UP pour garantir que le MCP23009E répond même si les tests sont lancés isolément.

@nedseb nedseb merged commit 08ca69b into main Mar 13, 2026
4 checks passed
@nedseb nedseb deleted the feature/board-qualification-framework branch March 13, 2026 11:26
@semantic-release-updater
Copy link
Copy Markdown

🎉 This PR is included in version 0.0.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request released test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tests: Extend test framework for driverless board qualification scenarios.

2 participants